﻿// @file kintl.c
#include "pch.h"
#include "sbcs.h"
#include "sbctn.h"
#include "extend/sbvalues.h"
#include <locale.h>

//////////////////////////////////////////////////////////////////////////
// 국제화

//
static struct kImplIntl
{
	kint				cp;
	char				locale[64];
} k_impl_intl =
{
	0,
	{0, },
};

//
static const kCpInfo k_impl_cps[] =
{
	{1, "C", 0x0409, KACP_OEM},							// C
	{2, "ar_SA", 0x1401, KACP_ARABIC},                  // 아랍
	{2, "bg_BG", 0x0402, KACP_CYRILLIC},                // 불가리아
	{5, "zh_CN", 0x0804, KACP_SIMPLIFIED_CHINESE},      // 중국
	{5, "zh_TW", 0x0404, KACP_TRADITIONAL_CHINESE},     // 대만
	{5, "zh_HK", 0x0404, KACP_TRADITIONAL_CHINESE},     // 홍콩
	{2, "cs_CZ", 0x0405, KACP_CENTRAL_LATIN},           // 체코
	{2, "da_DK", 0x0406, KACP_WESTERN_LATIN},           // 덴마크
	{2, "nl_NL", 0x0413, KACP_WESTERN_LATIN},           // 네덜란드
	{2, "en_US", 0x0409, KACP_OEM},                     // 영어권
	{2, "fi_FI", 0x040B, KACP_WESTERN_LATIN},           // 핀란드
	{2, "fr_FR", 0x040C, KACP_WESTERN_LATIN},           // 불어권
	{2, "de_DE", 0x0407, KACP_WESTERN_LATIN},           // 독어권
	{2, "el_GR", 0x0408, KACP_GREEK},                   // 그리스
	{2, "iw_IL", 0x040D, KACP_HEBREW},                  // 이스라엘(히랍어)
	{2, "hu_HU", 0x040E, KACP_CENTRAL_LATIN},           // 헝가리
	{2, "is_IS", 0x040F, KACP_WESTERN_LATIN},           // 아이슬란드
	{2, "it_IT", 0x0410, KACP_WESTERN_LATIN},           // 이태리어권
	{2, "ja_JP", 0x0411, KACP_JAPANESE},                // 일본
	{2, "ko_KR", 0x0412, KACP_KOREAN},                  // 한국
	{2, "no_NO", 0x0414, KACP_WESTERN_LATIN},           // 노르웨이
	{2, "pl_PL", 0x0415, KACP_CENTRAL_LATIN},           // 폴란드
	{2, "pt_PT", 0x0816, KACP_WESTERN_LATIN},           // 포르투갈
	{2, "ro_RO", 0x0418, KACP_CENTRAL_LATIN},           // 루마니아
	{2, "ru_RU", 0x0419, KACP_CYRILLIC},                // 러시아
	{2, "hr_HR", 0x041A, KACP_CENTRAL_LATIN},           // 크로아티아
	{2, "sk_SK", 0x041B, KACP_CENTRAL_LATIN},           // 슬로바키아
	{2, "sl_SL", 0x0424, KACP_CENTRAL_LATIN},           // 슬로베니아
	{2, "es_ES", 0x040A, KACP_WESTERN_LATIN},           // 에스빠냐
	{2, "sv_SE", 0x041D, KACP_WESTERN_LATIN},           // 스웨덴
	{2, "th_TH", 0x041E, KACP_THAI},                    // 태국
	{2, "tr_TR", 0x041F, KACP_TURKISH},                 // 터키
	{2, "vi_VN", 0x042B, KACP_VIETNAMESE},              // 베트남
	{4, "univ", 0x0409, KACP_OEM},						// 유니버설
	{6, "utf-16", 0, KACP_UTF16},						// UTF-16
	{5, "ucs-4", 0, KACP_UCS4},							// UCS-4
};

void _k_intl_init(void)
{
#if _SB_WINDOWS_
	kint i, lc, lcid;

	// LOCALE_ILANGUAGE LOCALE_IDEFAULTCODEPAGE
#if _SB_WINDOWS_DESKTOP_
	if (GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_RETURN_NUMBER | LOCALE_ILANGUAGE,
		(LPWSTR)&lcid, sizeof(lc) / sizeof(wchar_t)) == 0)
		lcid = 0x0409;
#else
	if (GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_RETURN_NUMBER | LOCALE_ILANGUAGE,
		(LPWSTR)&lcid, sizeof(lc) / sizeof(wchar_t)) == 0)
		lcid = 0x0409;
#endif

	// C를 패스해야 하므로 1부터 시작
	for (lc = 0, i = 1; i < (kint)K_COUNTOF(k_impl_cps); i++)
	{
		if (k_impl_cps[i].chcp == lcid)
		{
			lc = i;
			break;
		}
	}

	k_setcp(lc);
#else
	char sz[64];

	if (k_getenv("LANG", sz, 64 - 1) && sz[0])
		k_setlcp(sz);
	else
	{
		// 기본 코드 페이지를 C로 한다
		k_setlcp("C");
	}
#endif

	k_setlocale(NULL);
}

/**
 * 코드 페이지를 설정한다.
 * @param	cp	코드페이지.
 */
void k_setcp(kint cp)
{
	k_return_if_fail(cp >= 0 && cp < (kint)K_COUNTOF(k_impl_cps));

	k_impl_intl.cp = cp;
}

/**
 * 코드 페이지 문자열을 설정한다.
 * @param	sz	코드페이지 문자열.
 */
void k_setlcp(const char* sz)
{
	kint i, cp;

	k_return_if_fail(sz);

	// 찾기
	for (cp = i = 0; i < (kint)K_COUNTOF(k_impl_cps); i++)
	{
		if (k_strnicmp(sz, k_impl_cps[i].name, k_impl_cps[i].len) == 0)
		{
			cp = i;
			break;
		}
	}

#if !_SB_WINDOWS_ && !_SB_MOBILE_
	if (!k_stristr(sz, "UTF-8") && !k_stristr(sz, "UTF8"))
	{
		// UTF-8 지원 안하는가?!!
		fputs("sb.warning: no utf-8 support platform.\n", stderr);
	}
#endif

	k_setcp(cp);
}

/**
 * 로케일을 설정한다.
 * @param	locale	로캘 문자열.
 */
void k_setlocale(const char* locale)
{
#if _SB_ANDROID_
	// 안드로이드 PDK, NDK에서 로케일 함수 동작 안함 (2012-08-23, Android 4.0.4 기준)
	// 일단, 한국어로 하자고요
	k_strcpy(k_impl_intl.locale, "ko_KR.UTF-8");
#else
	const char* psz;
#if _MSC_VER
	const char* at;
#endif
	if (locale)
		psz = setlocale(LC_ALL, locale);
	else
	{
		// MSC, GLIBC 동작함
		// LIBC 확인해야함
		psz = setlocale(LC_ALL, "");

#if _MSC_VER
		// 윈도우는 'Korean_Korean.949' 이런 식이라 '_' 앞에꺼만 가져오자.
		at = k_strchr(psz, '_');

		if (at)
		{
			k_strncpy(k_impl_intl.locale, psz, at - psz);
			setlocale(LC_ALL, k_impl_intl.locale);
		}
#endif
	}

	k_strncpy(k_impl_intl.locale, psz, 63);
#endif
}

/**
 * 코드 페이지 정보를 얻는다.
 * @param [입력,반환]	cp	(널값이 아니면) 코드페이지 정보.
 */
void k_getcp(kCpInfo* cp)
{
	if (cp)
		*cp = k_impl_cps[k_impl_intl.cp];
}

/**
 * 아스키를 유니코드로 변환.
 * outuni 인수가 널이면 변환에 필요한 길이를 반환
 * @param [반환]	outuni	(널값이 아니면) 출력 유니코드 버퍼.
 * @param	outsize		  	출력 버퍼 크기.
 * @param	inasc		  	입력 아스키 버퍼.
 * @param	insize		  	입력 버퍼의 크기.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_asc_to_uni(kwchar* outuni, ksize_t outsize, const char* inasc, ksize_t insize)
{
#if _SB_WINCONV_
	int len = MultiByteToWideChar(CP_THREAD_ACP, 0, inasc, insize == 0 ? -1 : (int)insize, outuni, (int)outsize);

	if (outuni && len >= 0)
		outuni[len] = L'\0';

	return len;
#else
#if _SB_UNIX_
	// 유닉스 계열은 UTF-8 -> UCS-4만 쓴다고 가정하자.
	return k_conv_utf8_to_ucs4(outuni, outsize, inasc, insize);
#else
	return mbstowcs(outuni, inasc, outsize);
#endif
#endif
}

/**
 * 유니 코드를 아스키로 변환.
 * outasc 인수가 널이면 변환에 필요한 길이를 반환
 * @param [반환]	outasc	(널값이 아니면) 출력 아스키 버퍼.
 * @param	outsize		  	출력 버퍼 크기.
 * @param	inuni		  	입력 유니코드 버퍼.
 * @param	insize		  	입력 버퍼의 크기.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_uni_to_asc(char* outasc, ksize_t outsize, const kwchar* inuni, ksize_t insize)
{
#if _SB_WINCONV_
	int len = WideCharToMultiByte(CP_THREAD_ACP, 0, inuni, insize == 0 ? -1 : (int)insize, outasc, (int)outsize, NULL, NULL);

	if (outasc && len >= 0)
		outasc[len] = '\0';

	return len;
#else
#if _SB_UNIX_
	// 유닉스 계열은 UCS-4 -> UTF-8만 쓴다고 가정하자
	return k_conv_ucs4_to_utf8(outasc, outsize, inuni, insize);
#else
	return wcstombs(outasc, inuni, outsize);
#endif
#endif
}

/**
 * 아스키를 유니코드로 변환.
 * @param [반환]	outuni	(널값이 아니면) 출력 유니코드 버퍼.
 * @param	outsize		  	출력 버퍼의 크기.
 * @param	inasc		  	입력 아스키 버퍼.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 * @sa k_conv_asc_to_uni
 */
ksize_t k_getuni(kwchar* outuni, ksize_t outsize, const char* inasc)
{
	return k_conv_asc_to_uni(outuni, outsize, inasc, 0);
}

/**
 * 유니코드를 아스키로 변환.
 * @param [입력,반환]	outasc	(널값이 아니면) 출력 아스키 버퍼.
 * @param	outsize		  	출력 버퍼의 크기.
 * @param	inuni		  	입력 유니코드 버퍼.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 * @sa k_conv_uni_to_asc			
 */
ksize_t k_getasc(char* outasc, ksize_t outsize, const kwchar* inuni)
{
	return k_conv_uni_to_asc(outasc, outsize, inuni, 0);
}

/**
 * utf8 -> ucs4.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (ucs4).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (utf8).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_utf8_to_ucs4(kucs4char* dest, ksize_t destsize, const char* src, ksize_t srclen)
{
	k_return_value_if_fail(src, 0);

	if (destsize == 0 || !dest)
	{
		// utf-8 -> ucs-4의 길이는.... 그냥 utf-8의 길이.
		return k_utf8len(src);
	}
	else
	{
		const char* t;
		ksize_t i, size;

		if (srclen == 0)
			srclen = k_utf8len(src);

		size = K_MIN(destsize, srclen);

		for (t = src, i = 0; i < size; i++)
		{
			dest[i] = k_utf8ccnc(t);
			t = k_utf8chn(t);
		}

		dest[i] = (kucs4char)'\0';

		return size;
	}
}

/**
 * utf8 -> utf16.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (utf16).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (utf8).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_utf8_to_utf16(kucs2char* dest, ksize_t destsize, const char* src, ksize_t srclen)
{
	k_return_value_if_fail(src, 0);

	if (destsize == 0 || !dest)
	{
		// utf-8 -> ucs-4의 길이는.... 그냥 utf-8의 길이.
		// 다만 서로게이트 문제는 어떻게 하나... 일단 이걸로 하고 나중에 고치자
		return k_utf8len(src);
	}
	else
	{
		const char* t;
		ksize_t i, size;

		if (srclen <= 0)
			srclen = k_utf8len(src);

		size = K_MIN(destsize, srclen);

		for (t = src, i = 0; i < size;)
		{
			kucs4char ch = k_utf8ccnc(t);

			if (ch < 0x010000)
				dest[i++] = (kucs2char)ch;
			else
			{
				dest[i++] = (kucs2char)((ch - 0x010000) / 0x0400 + 0xD800);
				dest[i++] = (kucs2char)((ch - 0x010000) % 0x0400 + 0xDC00);
			}

			t = k_utf8chn(t);
		}

		dest[i] = (kucs2char)'\0';

		return size;
	}
}

/**
 * ucs4 -> utf8.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (utf8).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (ucs4).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_ucs4_to_utf8(char* dest, ksize_t destsize, const kucs4char* src, ksize_t srclen_org)
{
	ksize_t z, n, size;
	kssize_t i, srclen;
	kucs4char uc;

	k_return_value_if_fail(src, 0);

	srclen = (kssize_t)srclen_org;

	if (destsize > 0)
	{
		for (z = 0, size = 0, i = 0; srclen < 0 || i < srclen; i++)
		{
			uc = src[i];

			if (!uc)
				break;

			if (K_OFLAG(uc, 0x80000000))	// if (uc >= 0x80000000)
			{
				// 변경할 수 없는 문자가 포함됨..
				// 일단 그냥 못한다고 보내자
				return 0;
			}

			n = (uc < 0x80) ? 1 :
				(uc < 0x0800) ? 2 :
				(uc < 0x010000) ? 3 :
				(uc < 0x200000) ? 4 :
				(uc < 0x04000000) ? 5 :
				6;

			z = size + n;

			if (z > destsize)
				break;

			size = z;
		}
	}
	else
	{
		for (size = 0, i = 0; srclen < 0 || i < srclen; i++)
		{
			uc = src[i];

			if (!uc)
				break;

			if (K_OFLAG(uc, 0x80000000))	// if (uc >= 0x80000000)
			{
				// 변경할 수 없는 문자가 포함됨..
				// 일단 그냥 못한다고 보내자
				return 0;
			}

			n = (uc < 0x80) ? 1 :
				(uc < 0x0800) ? 2 :
				(uc < 0x010000) ? 3 :
				(uc < 0x200000) ? 4 :
				(uc < 0x04000000) ? 5 :
				6;

			size += n;
		}
	}

	if (dest && destsize > 0)
	{
		char* p = dest;

		for (i = 0; p < (dest + size); i++)
			p += k_utf8cunc(src[i], p);

		*p = '\0';
	}

	return size;
}

// utf16 서로게이트를 ucs4로 바꿈
K_INLINE kucs4char _k_utf16_surrogate(kucs2char h, kucs2char l)
{
	return (((kucs4char)h - 0xD800) * 0x0400 + (kucs4char)l - 0xDC00 + 0x010000);
}

/**
 * utf16 -> utf8.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (utf8).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (utf16).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_utf16_to_utf8(char* dest, ksize_t destsize, const kucs2char* src, ksize_t srclen)
{
	const kucs2char* cp;
	char* p;
	kucs4char hsg, uc;
	kucs2char ch;
	ksize_t size, n, z;

	k_return_value_if_fail(src, 0);

	cp = src;
	hsg = 0;
	size = 0;

	if (destsize > 0)
	{
		for (z = 0; (srclen == 0 || (ksize_t)(cp - src) < srclen) && *cp; cp++)
		{
			ch = *cp;

			if (ch >= 0xDC00 && ch < 0xE000)
			{
				// 하위 서로게이트
				if (hsg)
				{
					uc = _k_utf16_surrogate((kucs2char)hsg, ch);
					hsg = 0;
				}
				else
				{
					// 음... 변경할 수 없는 문자가 있네...
					// 일단 못한다고 보내자
					return 0;
				}
			}
			else
			{
				// 일반
				if (hsg)
				{
					// 상위 서로게이트가 있네?
					// 일단.. 문제가 있다고 판단
					return 0;
				}

				if (ch >= 0xD800 && ch < 0xDC00)
				{
					// 상위 서로게이트
					hsg = ch;
					continue;
				}

				uc = ch;
			}

			n = (uc < 0x80) ? 1 :
				(uc < 0x0800) ? 2 :
				(uc < 0x010000) ? 3 :
				(uc < 0x200000) ? 4 :
				(uc < 0x04000000) ? 5 :
				6;

			z = size + n;

			if (z > destsize)
				break;

			size = z;
		}
	}
	else
	{
		for (; (srclen == 0 || (ksize_t)(cp - src) < srclen) && *cp; cp++)
		{
			ch = *cp;

			if (ch >= 0xDC00 && ch < 0xE000)
			{
				// 하위 서로게이트
				if (hsg)
				{
					uc = _k_utf16_surrogate((kucs2char)hsg, ch);
					hsg = 0;
				}
				else
				{
					// 음... 변경할 수 없는 문자가 있네...
					// 일단 못한다고 보내자
					return 0;
				}
			}
			else
			{
				// 일반
				if (hsg)
				{
					// 상위 서로게이트가 있네?
					// 일단.. 문제가 있다고 판단
					return 0;
				}

				if (ch >= 0xD800 && ch < 0xDC00)
				{
					// 상위 서로게이트
					hsg = ch;
					continue;
				}

				uc = ch;
			}

			n = (uc < 0x80) ? 1 :
				(uc < 0x0800) ? 2 :
				(uc < 0x010000) ? 3 :
				(uc < 0x200000) ? 4 :
				(uc < 0x04000000) ? 5 :
				6;

			size += n;
		}
	}

	if (hsg)
	{
		// 하위 서로게이트가 남음...
		// 못함
		return 0;
	}

	if (destsize > 0 && dest)
	{
		hsg = 0;
		p = dest;
		cp = src;

		for (; p < dest + size; cp++)
		{
			ch = *cp;

			if (ch >= 0xDC00 && ch < 0xE000)
			{
				// 하위 서로게이트
				uc = _k_utf16_surrogate((kucs2char)hsg, ch);
				hsg = 0;
			}
			else if (ch >= 0xD800 && ch < 0xDC00)
			{
				// 상위 서로게이트
				hsg = ch;
				continue;
			}
			else
			{
				// 일반 글자
				uc = ch;
			}

			p += k_utf8cunc(uc, p);
		}

		*p = '\0';
	}

	return size;
}

/**
 * utf16 -> ucs4.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (ucs4).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (utf16).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_utf16_to_ucs4(kucs4char* dest, ksize_t destsize, const kucs2char* src, ksize_t srclen)
{
	const kucs2char* cp;
	kucs4char* p;
	kucs4char hsg, uc;
	kucs2char ch;
	ksize_t size, z;

	k_return_value_if_fail(src, 0);

	cp = src;
	hsg = 0;
	size = 0;

	if (destsize > 0)
	{
		for (z = 0; (srclen == 0 || (ksize_t)(cp - src) < srclen) && *cp; cp++)
		{
			ch = *cp;

			if (ch >= 0xDC00 && ch < 0xE000)
			{
				// 하위 서로게이트
				if (hsg)
				{
					uc = _k_utf16_surrogate((kucs2char)hsg, ch);
					hsg = 0;
				}
				else
				{
					// 음... 변경할 수 없는 문자가 있네...
					// 일단 못한다고 보내자
					return 0;
				}
			}
			else
			{
				// 일반
				if (hsg)
				{
					// 상위 서로게이트가 있네?
					// 일단.. 문제가 있다고 판단
					return 0;
				}

				if (ch >= 0xD800 && ch < 0xDC00)
				{
					// 상위 서로게이트
					hsg = ch;
					continue;
				}

				uc = ch;
			}

			z = size + 1;

			if (z > destsize)
				break;

			size = z;
		}
	}
	else
	{
		for (; (srclen == 0 || (ksize_t)(cp - src) < srclen) && *cp; cp++)
		{
			ch = *cp;

			if (ch >= 0xDC00 && ch < 0xE000)
			{
				// 하위 서로게이트
				if (hsg)
				{
					uc = _k_utf16_surrogate((kucs2char)hsg, ch);
					hsg = 0;
				}
				else
				{
					// 음... 변경할 수 없는 문자가 있네...
					// 일단 못한다고 보내자
					return 0;
				}
			}
			else
			{
				// 일반
				if (hsg)
				{
					// 상위 서로게이트가 있네?
					// 일단.. 문제가 있다고 판단
					return 0;
				}

				if (ch >= 0xD800 && ch < 0xDC00)
				{
					// 상위 서로게이트
					hsg = ch;
					continue;
				}

				uc = ch;
			}


			size++;
		}
	}

	if (hsg)
	{
		// 하위 서로게이트가 남음...
		// 못함
		return 0;
	}

	if (destsize > 0 && dest)
	{
		hsg = 0;
		p = dest;
		cp = src;

		for (; p < dest + size; cp++)
		{
			ch = *cp;

			if (ch >= 0xDC00 && ch < 0xE000)
			{
				// 하위 서로게이트
				uc = _k_utf16_surrogate((kucs2char)hsg, ch);
				hsg = 0;
			}
			else if (ch >= 0xD800 && ch < 0xDC00)
			{
				// 상위 서로게이트
				hsg = ch;
				continue;
			}
			else
			{
				// 일반 글자
				uc = ch;
			}

			*p = uc;
			p++;
		}

		*p = '\0';
	}

	return size;
}

/**
 * utf16 -> ucs4.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (utf16).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (ucs4).
 * @param	srclen_org		원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_ucs4_to_utf16(kucs2char* dest, ksize_t destsize, const kucs4char* src, ksize_t srclen_org)
{
	kucs2char* p;
	kucs4char uc;
	ksize_t size, z;
	kssize_t i, srclen;

	k_return_value_if_fail(src, 0);

	size = 0;
	srclen = (kssize_t)srclen_org;

	if (destsize > 0)
	{
		for (i = 0; (srclen < 0 || i < srclen) && src[i]; i++)
		{
			uc = src[i];

			if (uc < 0xD800)
				z = 1;
			else if (uc < 0xE000)
			{
				// 음..
				return 0;
			}
			else if (uc < 0x010000)
				z = 1;
			else if (uc < 0x110000)
				z = 2;
			else
			{
				// 음...
				return 0;
			}

			z += size;

			if (z > destsize)
				break;

			size = z;
		}
	}
	else
	{
		for (i = 0; (srclen < 0 || i < srclen) && src[i]; i++)
		{
			uc = src[i];

			if (uc < 0xD800)
				size++;
			else if (uc < 0xE000)
			{
				// 음..
				return 0;
			}
			else if (uc < 0x010000)
				size++;
			else if (uc < 0x110000)
				size += 2;
			else
			{
				// 음...
				return 0;
			}
		}
	}

	if (destsize > 0 && dest)
	{
		for (p = dest, i = 0, z = 0; z < size; i++)
		{
			uc = src[i];

			if (uc < 0x010000)
				p[z++] = (kucs2char)uc;
			else
			{
				p[z++] = (kucs2char)((uc - 0x010000) / 0x0400 + 0xD800);
				p[z++] = (kucs2char)((uc - 0x010000) % 0x0400 + 0xDC00);
			}
		}

		p[z] = (kucs2char)'\0';
	}

	return size;
}

/**
 * utf8 -> asc.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * 이 함수는 유닉스같이 UTF-8 코드 체계에서는 단순히 k_strcpy 함수와 비슷한 기능을 한다
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (asc).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (utf8).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_utf8_to_asc(char* dest, ksize_t destsize, const char* src, ksize_t srclen)
{
	ksize_t size;
	kwchar* wcs;

#if _SB_WINDOWS_
	size = k_conv_utf8_to_utf16(NULL, 0, src, srclen);
	k_return_value_if_fail(size > 0, 0);

	wcs = k_new(size + 4, kwchar);
	k_conv_utf8_to_utf16(wcs, size + 2, src, srclen);
#else
#if _SB_UNIX_
	// 유닉스 UTF-8 = ASC
	k_return_value_if_fail(src, 0);

	if (srclen <= 0)
		srclen = k_strlen(src);

	size = K_MIN(destsize, srclen);

	if (dest)
		k_strncpy(dest, src, size);

	return size;
#else
	size = k_conv_utf8_to_ucs4(NULL, 0, src, srclen);
	k_return_value_if_fail(size > 0, 0);

	wcs = k_new(size + 4, kwchar);
	k_conv_utf8_to_ucs4(wcs, size + 2, src, srclen);
#endif
#endif

	size = k_conv_uni_to_asc(dest, destsize, wcs, size);

	k_delete(wcs);

	return size;
}

/**
 * utf8 -> uni.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * 이 함수는 윈도우에서는 utf8 -> utf16이고 유닉스에서는 utf8 -> ucs4 기능을 한다.
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (wchar_t).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (utf8).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_utf8_to_uni(kwchar* dest, ksize_t destsize, const char* src, ksize_t srclen)
{
#if _SB_WINDOWS_
	return k_conv_utf8_to_utf16(dest, destsize, src, srclen);
#else
	return k_conv_utf8_to_ucs4(dest, destsize, src, srclen);
#endif
}

/**
 * asc -> utf8.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (utf8).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (asc).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_asc_to_utf8(char* dest, ksize_t destsize, const char* src, ksize_t srclen)
{
	ksize_t size;
	kwchar* wcs;

#if !_SB_WINDOWS_ && _SB_UNIX_
	k_return_value_if_fail(src, 0);

	if (srclen <= 0)
		srclen = k_strlen(src);

	size = K_MIN(destsize, (ksize_t)srclen);

	if (dest)
		k_strncpy(dest, src, size);

	return size;
#endif

	size = k_conv_asc_to_uni(NULL, 0, src, srclen);
	k_return_value_if_fail(size > 0, 0);

	wcs = k_new(size + 4, kwchar);
	k_conv_asc_to_uni(wcs, size + 2, src, srclen);

#if _SB_WINDOWS_
	size = k_conv_utf16_to_utf8(dest, destsize, wcs, (ksize_t)size);
#else
	size = k_conv_ucs4_to_utf8(dest, destsize, wcs, (ksize_t)size);
#endif

	k_delete(wcs);

	return size;
}

/**
 * uni -> utf8.
 * 대상 버퍼가 널값이면 변환에 필요한 길이를 반환
 * 이 함수는 윈도우에서는 utf16 -> utf8이고 유닉스에서는 ucs4 -> utf8 기능을 한다.
 * @param [반환]	dest	(널값이 아니면) 대상 버퍼 (utf8).
 * @param	destsize		대상 버퍼 크기.
 * @param	src				원본 (wchar_t).
 * @param	srclen			원본 길이. 0으로 지정할 수 있음.
 * @return	변환한 길이 또는 변환에 필요한 길이.
 */
ksize_t k_conv_uni_to_utf8(char* dest, ksize_t destsize, const kwchar* src, ksize_t srclen)
{
#if _SB_WINCONV_
	return k_conv_utf16_to_utf8(dest, destsize, src, srclen);
#else
	return k_conv_ucs4_to_utf8(dest, destsize, src, srclen);
#endif
}

// b64 char
static const char k_impl_b64[] =
{
	"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
	"abcdefghijklmnopqrstuvwxyz" \
	"0123456789+/"
};

/**
 * base64로 인코딩 한다.
 * 반환된 데이터인 dest는 k_free 함수로 사용후 해제해야 한다.
 * @param [반환]	dest	(널값이 아니면) 인코딩 결과 버퍼.
 * @param	src				원본.
 * @param	srclen			원본의 길이. 이 값은 0이어도 된다.
 * @return	변환된 내용의 길이.
 */
ksize_t k_base64_encrypt_alloc(char** dest, const char* src, ksize_t srclen)
{
	kByteArr arr;
	kint i, z;
	kbyte c3[3], c4[4];

	k_return_value_if_fail(src, 0);
	k_return_value_if_fail(dest, 0);

	if (srclen == 0)
		srclen = k_strlen(src);

	k_arr_init(kByteArr, &arr, srclen * 2);
	i = z = 0;

	while (srclen--)
	{
		c3[i++] = *(src++);

		if (i == 3)
		{
			c4[0] = (c3[0] & 0xFC) >> 2;
			c4[1] = ((c3[0] & 0x03) << 4) | ((c3[1] & 0xF0) >> 4);
			c4[2] = ((c3[1] & 0x0F) << 2) | ((c3[2] & 0xC0) >> 6);
			c4[3] = c3[2] & 0x3F;

			for (i = 0; i < 4; i++)
			{
				kbyte ch = (kbyte)k_impl_b64[c4[i]];
				k_arr_add(kByteArr, &arr, ch);
			}

			i = 0;
		}
	}

	if (i)
	{
		for (z = i; z < 3; z++)
			c3[z] = '\0';

		c4[0] = (c3[0] & 0xFC) >> 2;
		c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xF0) >> 4);
		c4[2] = ((c3[1] & 0x0F) << 2) + ((c3[2] & 0xC0) >> 6);
		c4[3] = c3[2] & 0x3F;

		for (z = 0; z < (i + 1); z++)
		{
			kbyte ch = (kbyte)k_impl_b64[c4[z]];
			k_arr_add(kByteArr, &arr, ch);
		}

		while ((i++ < 3))
			k_arr_add(kByteArr, &arr, '=');
	}

	k_arr_add(kByteArr, &arr, '\0');
	*dest = (char*)arr.data;

	return arr.count - 1;
}

/**
 * base64를 디코딩 한다.
 * 반환된 데이터인 dest는 k_free 함수로 사용후 해제해야 한다.
 * @param [반환]	dest	(널값이 아니면) 디코딩 결과 버퍼.
 * @param	src				원본.
 * @param	srclen			원본이 길이. 이 값은 0이어도 상관없다.
 * @return	디코딩된 데이터의 길이.
 */
ksize_t k_base64_decrypt_alloc(char** dest, const char* src, ksize_t srclen)
{
	kByteArr arr;
	kint i, z, at;
	kbyte c3[3], c4[4];

	k_return_value_if_fail(src, 0);
	k_return_value_if_fail(dest, 0);

	if (srclen == 0)
		srclen = k_strlen(src);

	k_arr_init(kByteArr, &arr, srclen * 2);
	i = z = at = 0;

	while (srclen-- && src[at] != '=' && (k_isalnum(src[at]) || src[at] == '+' || src[at] == '/'))
	{
		c4[i++] = src[at++];

		if (i == 4)
		{
			for (i = 0; i < 4; i++)
			{
				const char* p = k_strchr(k_impl_b64, c4[i]);
				c4[i] = (kbyte)(p - k_impl_b64);
			}

			c3[0] = (c4[0] << 2) | ((c4[1] & 0x30) >> 4);
			c3[1] = ((c4[1] & 0x0F) << 4) | ((c4[2] & 0x3C) >> 2);
			c3[2] = ((c4[2] & 0x03) << 6) | c4[3];

			for (i = 0; i < 3; i++)
				k_arr_add(kByteArr, &arr, c3[i]);

			i = 0;
		}
	}

	if (i)
	{
		for (z = i; z < 4; z++)
			c4[z] = 0;

		for (z = 0; z < 4; z++)
		{
			const char* p = k_strchr(k_impl_b64, c4[z]);
			c4[z] = (kbyte)(p - k_impl_b64);
		}

		c3[0] = (c4[0] << 2) | ((c4[1] & 0x30) >> 4);
		c3[1] = ((c4[1] & 0x0F) << 4) | ((c4[2] & 0x3C) >> 2);
		c3[2] = ((c4[2] & 0x03) << 6) | c4[3];

		for (z = 0; z < i - 1; z++)
			k_arr_add(kByteArr, &arr, c3[z]);
	}

	k_arr_add(kByteArr, &arr, '\0');
	*dest = (char*)arr.data;

	return arr.count - 1;
}

/**
 * base64를 디코딩 한다.
 * @param [반환]	dest	(널값이 아니면) 디코딩 결과 버퍼.
 * @param	src				원본.
 * @param	srclen			원본이 길이. 이 값은 0이어도 상관없다.
 * @return	디코딩된 데이터의 길이.
 */
ksize_t k_base64_decrypt(char* dest, ksize_t destsize, const char* src, ksize_t srclen)
{
	char* ptr;
	kint i, z, at, cnt;
	kbyte c3[3], c4[4];

	k_return_value_if_fail(src, 0);

	if (srclen == 0)
		srclen = k_strlen(src);

	i = z = at = 0;

	if (destsize > 0 && dest)
	{
		ptr = dest;

		while (srclen-- && src[at] != '=' && (k_isalnum(src[at]) || src[at] == '+' || src[at] == '/'))
		{
			c4[i++] = src[at++];

			if (i == 4)
			{
				for (i = 0; i < 4; i++)
				{
					const char* p = k_strchr(k_impl_b64, c4[i]);
					c4[i] = (kbyte)(p - k_impl_b64);
				}

				c3[0] = (c4[0] << 2) | ((c4[1] & 0x30) >> 4);
				c3[1] = ((c4[1] & 0x0F) << 4) | ((c4[2] & 0x3C) >> 2);
				c3[2] = ((c4[2] & 0x03) << 6) | c4[3];

				for (i = 0; i < 3; i++)
				{
					*ptr++ = c3[i];

					if ((ksize_t)((ptr - dest) + 1) >= destsize)
						goto pos_break;
				}

				i = 0;
			}
		}

		if (i)
		{
			for (z = i; z < 4; z++)
				c4[z] = 0;

			for (z = 0; z < 4; z++)
			{
				const char* p = k_strchr(k_impl_b64, c4[z]);
				c4[z] = (kbyte)(p - k_impl_b64);
			}

			c3[0] = (c4[0] << 2) | ((c4[1] & 0x30) >> 4);
			c3[1] = ((c4[1] & 0x0F) << 4) | ((c4[2] & 0x3C) >> 2);
			c3[2] = ((c4[2] & 0x03) << 6) | c4[3];

			for (z = 0; z < i - 1; z++)
			{
				*ptr++ = c3[z];

				if ((ksize_t)((ptr - dest) + 1) >= destsize)
					break;
			}
		}

pos_break:
		*ptr = '\0';

		return ptr - dest;
	}
	else
	{
		cnt = 0;

		while (srclen-- && src[at] != '=' && (k_isalnum(src[at]) || src[at] == '+' || src[at] == '/'))
		{
			i++;
			at++;

			if (i == 4)
			{
				cnt += 3;
				i = 0;
			}
		}

		if (i)
			cnt += i - 1;

		return cnt;
	}
}

/**
 * base64를 인코딩 한다.
 * @param [반환]	dest	(널값이 아니면) 인코딩 결과 버퍼.
 * @param	src				원본.
 * @param	srclen			원본의 길이. 이 값은 0이어도 된다.
 * @return	변환된 내용의 길이.
 */
ksize_t k_base64_encrypt(char* dest, ksize_t destsize, const char* src, ksize_t srclen)
{
	char* ptr;
	kint i, z, cnt;
	kbyte c3[3], c4[4];

	k_return_value_if_fail(src, 0);

	if (srclen == 0)
		srclen = k_strlen(src);

	i = z = 0;

	if (destsize > 0 && dest)
	{
		ptr = dest;

		while (srclen--)
		{
			c3[i++] = *(src++);

			if (i == 3)
			{
				c4[0] = (c3[0] & 0xFC) >> 2;
				c4[1] = ((c3[0] & 0x03) << 4) | ((c3[1] & 0xF0) >> 4);
				c4[2] = ((c3[1] & 0x0F) << 2) | ((c3[2] & 0xC0) >> 6);
				c4[3] = c3[2] & 0x3F;

				for (i = 0; i < 4; i++)
				{
					kbyte ch = (kbyte)k_impl_b64[c4[i]];
					*ptr++ = ch;

					if ((ksize_t)((ptr - dest) + 1) >= destsize)
						goto pos_break;
				}

				i = 0;
			}
		}

		if (i)
		{
			for (z = i; z < 3; z++)
				c3[z] = '\0';

			c4[0] = (c3[0] & 0xFC) >> 2;
			c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xF0) >> 4);
			c4[2] = ((c3[1] & 0x0F) << 2) + ((c3[2] & 0xC0) >> 6);
			c4[3] = c3[2] & 0x3F;

			for (z = 0; z < (i + 1); z++)
			{
				kbyte ch = (kbyte)k_impl_b64[c4[z]];
				*ptr++ = ch;

				if ((ksize_t)((ptr - dest) + 1) >= destsize)
					goto pos_break;
			}

			while ((i++ < 3))
			{
				*ptr++ = '=';

				if ((ksize_t)((ptr - dest) + 1) >= destsize)
					goto pos_break;
			}
		}

pos_break:
		*ptr = '\0';

		return ptr - dest;
	}
	else
	{
		cnt = 0;

		while (srclen--)
		{
			i++;

			if (i == 3)
			{
				cnt += 4;
				i = 0;
			}
		}

		if (i)
		{
			for (z = 0; z < (i + 1); z++)
				cnt++;

			while ((i++ < 3))
				cnt++;
		}

		return cnt;
	}
}
